package vip.liux.backend.infrastructure.jpa.files;

import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import org.jetbrains.annotations.Nullable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import vip.liux.contracts.models.files.FileDescriptor;
import vip.liux.contracts.repositories.files.FileDescriptorRepository;
import vip.liux.contracts.shared.enums.file.FileType;

import java.util.List;

/**
 * FileDescriptorJpaRepository 是一个 JPA 仓库接口，
 * 用于操作 FileDescriptor 实体。
 * 它实现了 JpaSpecificationExecutor 以支持动态查询。
 */
public interface FileDescriptorJpaRepository extends JpaRepository<FileDescriptor, Long>, FileDescriptorRepository, JpaSpecificationExecutor<FileDescriptor> {

    /**
     * 构建 directoryId 的查询条件谓词。
     * 如果 directoryId 为 null，则返回 directory 字段为 null 的条件。
     * 如果 directoryId 不为 null，则返回 directory.id 等于 directoryId 的条件。
     *
     * @param c           CriteriaBuilder 用于构建查询条件
     * @param r           查询的根实体
     * @param directoryId 目录 id
     * @return 构建的谓词
     */
    private Predicate buildDirectoryPredicate(CriteriaBuilder c, Root<FileDescriptor> r, @Nullable Long directoryId) {
        if (directoryId == null) {
            return c.isNull(r.get("directory"));
        } else {
            return c.equal(r.get("directory").get("id"), directoryId);
        }
    }

    /**
     * 根据名称和目录 id 查找文件。
     *
     * @param name        文件名称
     * @param directoryId 目录 id
     * @return 匹配的 FileDescriptor 实体
     */
    @Override
    default FileDescriptor findByName(String name, @Nullable Long directoryId) {
        return findOne((r, q, c) -> {
            List<Predicate> predicates = new java.util.ArrayList<>();
            predicates.add(c.equal(r.get("name"), name));
            predicates.add(buildDirectoryPredicate(c, r, directoryId));
            return c.and(predicates.toArray(new Predicate[0]));
        }).orElse(null);
    }

    /**
     * 获取指定目录下的文件列表，支持过滤和排序。
     *
     * @param directoryId 目录 id
     * @param filter      名称过滤条件
     * @param sort        排序条件
     * @return 文件列表
     */
    @Override
    default List<FileDescriptor> getList(@Nullable Long directoryId, String filter, Sort sort) {
        return findAll((r, q, c) -> {
            List<Predicate> predicates = new java.util.ArrayList<>();
            predicates.add(buildDirectoryPredicate(c, r, directoryId));
            if (filter != null) {
                predicates.add(c.like(r.get("name"), "%" + filter + "%"));
            }
            return c.and(predicates.toArray(new Predicate[0]));
        }, sort);
    }


    /**
     * 获取指定目录下的文件分页列表，支持过滤和文件类型。
     *
     * @param directoryId 目录 id
     * @param fileType    文件类型
     * @param filter      名称过滤条件
     * @param page        分页条件
     * @return 文件分页列表
     */
    default Page<FileDescriptor> getPage(@Nullable Long directoryId, FileType fileType, String filter, Pageable page) {
        return findAll((r, q, c) -> {
            List<Predicate> predicates = new java.util.ArrayList<>();
            predicates.add(buildDirectoryPredicate(c, r, directoryId));
            if (filter != null) {
                predicates.add(c.like(r.get("name"), "%" + filter + "%"));
            }
            if (fileType != null) {
                predicates.add(c.equal(r.get("fileType"), fileType));
            }
            return c.and(predicates.toArray(new Predicate[0]));
        }, page);
    }

    /**
     * 获取指定目录下的文件数量。
     *
     * @param directoryId 目录 id
     * @return 文件数量
     */
    @Override
    default int countDirectoryFiles(@Nullable Long directoryId) {
        return (int) count((r, q, c) -> buildDirectoryPredicate(c, r, directoryId));
    }

    /**
     * 获取所有文件的总大小。
     *
     * @return 文件总大小
     */
    @Query("select sum(fd.size) from FileDescriptor fd")
    @Override
    long getTotalSize();

    /**
     * 获取指定目录下的文件总数
     *
     * @param directoryId 目录 id
     * @return 文件总大小
     */
    default long findByDirectoryId(@Nullable Long directoryId) {
        return count((r, q, c) -> {
            List<Predicate> predicates = new java.util.ArrayList<>();
            predicates.add(buildDirectoryPredicate(c, r, directoryId));
            return c.and(predicates.toArray(new Predicate[0]));
        });
    }
}
